﻿using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace files_merge
{
    /// <summary>
    /// 添加多个文件窗口
    /// </summary>
    public partial class AddFilesForm : Form
    {
        // 是否自定义地址
        private static bool is_custom_addr = false;
        // 是否是hex格式的地址
        private static bool is_hex_addr = false;
        // 保存父窗口工作区
        private Rectangle parent_rect;
        // 保存输入参数
        private AddFilesFormInArgs in_args;
        // 保存输出参数
        private AddFilesFormOutArgs out_args;

        // 页面输出参数，用于传递返回值
        public AddFilesFormOutArgs OutArgs { get; set; }

        /// <summary>
        /// 导入动态库，设置鼠标位置
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern bool SetCursorPos(int x, int y);

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="rectParentForm"></param>
        public AddFilesForm(Rectangle rectParentForm, AddFilesFormInArgs inArgs)
        {
            // 初始化各组件
            InitializeComponent();
            // 禁用放缩
            MaximumSize = MinimumSize = Size;
            // 取消最大化框
            MaximizeBox = false;
            // 窗口位于屏幕中心
            StartPosition = FormStartPosition.Manual;
            int work_area_t = rectParentForm.Top;
            int work_area_l = rectParentForm.Left;
            int work_area_w = rectParentForm.Width;
            int work_area_h = rectParentForm.Height;
            Location = new Point(work_area_l + (work_area_w - Size.Width) / 2, work_area_t + (work_area_h - Size.Height) / 2);
            // 判断是否需要自定义地址和选择地址模式
            chkCustomAddr.Checked = is_custom_addr;
            chkHexFormat.Checked = is_hex_addr;
            if (is_custom_addr)
            {
                txbOffsetAddr.Enabled = true;
                txbOffsetAddr.Focus();
            }
            else
            {
                txbOffsetAddr.Enabled = false;
            }
            // 保存输入参数
            in_args = inArgs;
            parent_rect = rectParentForm;
            // 加载地址进文本框
            if (is_hex_addr)
            {
                txbOffsetAddr.MaxLength = 8;
                txbOffsetAddr.Text = inArgs.OffsetAddr.ToString("X");
            }
            else
            {
                txbOffsetAddr.MaxLength = 10;
                txbOffsetAddr.Text = inArgs.OffsetAddr.ToString();
            }

            // 确定键不可用
            btnConfirm.Enabled = false;
            // 默认是取消状态
            DialogResult = DialogResult.Cancel;
        }

        /// <summary>
        /// 是否自定义地址选择框发生变化处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void chkCustomAddr_CheckedChanged(object sender, EventArgs e)
        {
            is_custom_addr = chkCustomAddr.Checked;
            if (is_custom_addr)
            {
                txbOffsetAddr.Enabled = true;
                txbOffsetAddr.Focus();
            }
            else
            {
                txbOffsetAddr.Enabled = false;
                if (is_hex_addr)
                {
                    txbOffsetAddr.Text = in_args.OffsetAddr.ToString("X");
                }
                else
                {
                    txbOffsetAddr.Text = in_args.OffsetAddr.ToString();
                }
            }
        }

        /// <summary>
        /// 是否HEX地址选择框发生变化处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void chkHexFormat_CheckedChanged(object sender, EventArgs e)
        {
            is_hex_addr = chkHexFormat.Checked;
            uint value;
            // 空白处理
            if (string.IsNullOrEmpty(txbOffsetAddr.Text))
            {
                txbOffsetAddr.Text = "0";
                txbOffsetAddr.Select(txbOffsetAddr.Text.Length, 0);
                txbOffsetAddr.ScrollToCaret();
            }
            if (is_hex_addr)
            {
                // 10进制转16进制
                value = Convert.ToUInt32(txbOffsetAddr.Text);
                txbOffsetAddr.MaxLength = 8;
                txbOffsetAddr.Text = value.ToString("X");
            }
            else
            {
                // 16进制转10进制
                value = uint.Parse(txbOffsetAddr.Text, System.Globalization.NumberStyles.HexNumber);
                txbOffsetAddr.MaxLength = 10;
                txbOffsetAddr.Text = value.ToString();
            }
            txbOffsetAddr.Focus();
        }

        /// <summary>
        /// 地址输入框输入值处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void txbOffsetAddr_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (char.IsNumber(e.KeyChar) ||
                char.IsControl(e.KeyChar) ||
                (is_hex_addr && (e.KeyChar >= 'A') && (e.KeyChar <= 'F')) ||
                (is_hex_addr && (e.KeyChar >= 'a') && (e.KeyChar <= 'f')))
            {
                e.Handled = false;
            }
            else
            {
                e.Handled = true;
            }
        }

        /// <summary>
        /// 地址输入框值变化处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void txbOffsetAddr_TextChanged(object sender, EventArgs e)
        {
            // 判断是否为空
            if (string.IsNullOrEmpty(txbOffsetAddr.Text))
            {
                txbOffsetAddr.Text = "0";
                txbOffsetAddr.Select(txbOffsetAddr.Text.Length, 0);
                txbOffsetAddr.ScrollToCaret();
                return;
            }
            else if (txbOffsetAddr.Text != "0")
            {
                txbOffsetAddr.Text = Regex.Replace(txbOffsetAddr.Text, "^0*", "");
                txbOffsetAddr.Select(txbOffsetAddr.Text.Length, 0);
                txbOffsetAddr.ScrollToCaret();
            }

            // 10进制需要判断是否大于32位数
            if (!is_hex_addr)
            {
                ulong value = Convert.ToUInt64(txbOffsetAddr.Text);
                if (value > uint.MaxValue)
                {
                    // 超出值范围，去除末尾数字
                    txbOffsetAddr.Text = txbOffsetAddr.Text.Substring(0, txbOffsetAddr.Text.Length - 1);
                    txbOffsetAddr.Select(txbOffsetAddr.Text.Length, 0);
                    txbOffsetAddr.ScrollToCaret();
                }
            }
        }

        /// <summary>
        /// 设置光标到按键中心
        /// </summary>
        /// <param name="btn"></param>
        private void SetCursorInButtonCenter(Button btn)
        {
            Point pt = new Point();
            pt = btn.PointToScreen(pt);
            // 设置到鼠标位置
            SetCursorPos(pt.X + btn.Width / 2,
                         pt.Y + btn.Height / 2);
        }

        /// <summary>
        /// “浏览...”按钮事件处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            // 创建打开文件对话框
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "二进制文件(*.bin)|*.bin|所有文件(*.*)|*.*";
            ofd.Title = "选择文件";
            ofd.Multiselect = true;

            // 如果没有确认打开文件，退出
            if (ofd.ShowDialog() != DialogResult.OK)
            {
                // 获取光标
                SetCursorInButtonCenter(btnOpenFile);
                return;
            }

            // 遍历所有文件
            txbFilePath.Clear();
            out_args.FileLength = new uint[ofd.FileNames.Length];
            out_args.FileName = new string[ofd.FileNames.Length];
            long offset_addr = in_args.OffsetAddr;
            uint count = 0;
            foreach (string name in ofd.FileNames)
            {
                // 获取文件信息
                FileInfo fi = new FileInfo(name);

                offset_addr += fi.Length;
                // 选择的文件路径
                txbFilePath.AppendText(name + "\r\n");
                // 设置文件信息输出参数
                out_args.FileLength[count] = (uint)fi.Length;
                out_args.FileName[count] = name;
                count++;
            }
            // 文件个数
            out_args.FileNum = count;
            // 确定键可用
            btnConfirm.Enabled = true;
            // 确定键获取焦点
            btnConfirm.Focus();
            // 获取光标
            SetCursorInButtonCenter(btnConfirm);
        }

        /// <summary>
        /// “取消”按钮事件处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            // 释放资源
            Dispose();
            // 关闭窗口
            Close();

            // 返回Cancel
            DialogResult = DialogResult.Cancel;
        }

        /// <summary>
        /// “确定”按钮事件处理
        /// </summary>
        /// <param name="sender">事件发送控件</param>
        /// <param name="e">事件参数</param>
        private void btnConfirm_Click(object sender, EventArgs e)
        {
            // 设置文件注释
            //out_args.FileComment = txbComment.Text;
            // 计算偏移地址
            uint value;
            if (is_hex_addr)
            {
                // 16进制转10进制
                value = uint.Parse(txbOffsetAddr.Text, System.Globalization.NumberStyles.HexNumber);
            }
            else
            {
                // 10进制转16进制
                value = Convert.ToUInt32(txbOffsetAddr.Text);
            }

            // 设置偏移地址
            out_args.OffsetAddr = value;

            // 输出参数
            OutArgs = out_args;

            // 返回OK
            DialogResult = DialogResult.OK;
        }
    }

    /// <summary>
    /// 添加文件框输入参数
    /// </summary>
    public struct AddFilesFormInArgs
    {
        // 文件放置偏移地址
        public uint OffsetAddr;
    }

    /// <summary>
    /// 添加文件框输出参数
    /// </summary>
    public struct AddFilesFormOutArgs
    {
        // 文件个数
        public uint FileNum;
        // 文件放置偏移地址
        public uint OffsetAddr;
        // 文件长度列表
        public uint[] FileLength;
        // 文件名列表
        public string[] FileName;
    }
}
